/*******************************************************************************
 * Copyright 2007, 2009 InnovaSoft SA http://www.innovasoft.biz/
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
 
package biz.innovasoft.nodb.query;

import java.util.Date;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.lucene.document.DateTools;
import org.apache.lucene.document.NumberTools;

public class Expression {

	public Operator operator;
	public Object attr;
	public Object value;

	public Object min, max;

	public Expression left;
	public Expression right;

	public Expression() {
	}

	public Expression(Object attr, Object value) {
		this(Operator.EQUAL, attr, value);
	}

	public Expression(Operator operator, Object value) {
		this.operator = operator;
		this.value = value;
	}

	public Expression(Operator operator, Object attr, Object value) {
		this.operator = operator;
		this.attr = attr;
		this.value = value;
	}

	public Expression(Operator operator, Object attr, Object min, Object max) {
		this.operator = operator;
		this.attr = attr;
		this.min = min;
		this.max = max;
	}

	public Expression(Operator operator, Expression left, Expression right) {
		this.operator = operator;
		this.left = left;
		this.right = right;
	}

	public Expression eq(Object attr, Object value) {
		return new Expression(Operator.EQUAL, attr, value);
	}

	public Expression and(Expression right) {
		return new Expression(Operator.AND, this, right);
	}

	public Expression not(Expression right) {
		return new Expression(Operator.NOT, this, right);
	}

	public Expression not(Object value) {
		return new Expression(Operator.NOT, value);
	}

	public Expression not(Object attr, Object value) {
		return new Expression(Operator.NOT, attr, value);
	}

	public Expression or(Expression right) {
		return new Expression(Operator.OR, this, right);
	}

	public Expression range(Object attr, Object min, Object max) {
		return new Expression(Operator.RANGE, attr, min, max);
	}

	public MultinaryExpression and(Expression... expressions) {
		return new MultinaryExpression(Operator.AND, this, expressions);
	}

	public MultinaryExpression or(Expression... expressions) {
		return new MultinaryExpression(Operator.OR, this, expressions);
	}

	public String toString() {
		StringBuffer buffer = new StringBuffer();

		switch (operator) {
		case EQUAL:
			return attr + ":" + objectToString(value);
		case AND:
			if (left != null) {
				buffer.append(left.toString());
			}
			buffer.append(" AND ");
			if (right != null) {
				buffer.append(right.toString());
			}
			break;
		case OR:
			if (left != null) {
				buffer.append(left.toString());
			}
			buffer.append(" OR ");
			if (right != null) {
				buffer.append(right.toString());
			}
			break;
		case NOT:
			buffer.append("-");
			if (right != null) {
				buffer.append(right.toString());
			} else if (attr != null) {
				buffer.append(attr);
				buffer.append(":");
				buffer.append(value);
			} else {
				buffer.append(value);
			}
			break;
		case RANGE:
			buffer.append(attr);
			buffer.append(":");
			buffer.append("[");
			buffer.append(objectToString(min));
			buffer.append(" TO ");
			buffer.append(objectToString(max));
			buffer.append("]");
		}
		return buffer.toString();
	}
	
	private String objectToString (Object value) {
		if (value instanceof Integer) {
			return NumberTools.longToString((Integer)value);
		}
		if (value instanceof Long) {
			return NumberTools.longToString((Long)value);
		}
		if (value instanceof Date) {
			return DateTools.dateToString ((Date)value, DateTools.Resolution.DAY);
		}
		return (String)value.toString().replaceAll(" ", "");
	}
	
	public boolean equals(Object obj) {
		return EqualsBuilder.reflectionEquals(this, obj);
	}

	public int hashCode() {
		return HashCodeBuilder.reflectionHashCode(this);
	}

}
